#!/usr/bin/env python

import os, rgma, sys, optparse

HIST_RETEN_TOKEN = 'HRP'
TABLES_TOKEN = 'TABLES'
TYPE_TOKEN = 'TYPE'
LOGICALNAME_TOKEN = 'LOGICALNAME'
PREDICATE_TOKEN = 'PREDICATE'

def main():    
    
    parser = optparse.OptionParser(usage="""usage:

  %prog start idFileName configFileName

     to start the secondary producer. Termination interval in
     seconds is returned as stdout. The file specified as
     idFileName must not exist.

  %prog ping idFileName

      to ping the secondary producer. It must be done at intervals
      of no more than the termination interval returned by the
      start command.

  %prog stop idFileName

      to stop the secondary producer and remove the file specified
      as idFileName.

    """, description="""
    Configures and controls a secondary producer.
    """)

    options, args = parser.parse_args()

    if len(args) < 1:
        terminate("The first parameter must be: 'start', 'stop' or 'ping'")
        
    cmd = args[0]
    args = args[1:]
        
    try:
        if cmd == "start":
            if len(args) != 2:
                terminate("arguments should be: start idFileName configFileName")
            idFileName, configFileName = args
            if os.path.isfile(idFileName):
                terminate("Error, idFile '%s' already exists\n" % idFileName)
            if not os.path.isfile(configFileName):
                terminate("Error, configFile '%s' does not exist\n" % configFileName)
            print start(idFileName, configFileName)
        elif cmd == "ping":
            if len(args) != 1:
                terminate("argument should be: ping idFileName")
            idFileName, = args
            ping(idFileName)
        elif cmd == "stop":
            if len(args) != 1:
                terminate("argument should be: stop idFileName")
            idFileName, = args
            stop(idFileName)
        else:
            terminate("The first parameter must be: 'start', 'stop' or 'ping'")
            
    except SPError, e:
        terminate(e.args[0])
        
def terminate(string):
    print >> sys.stderr, string
    sys.exit(1)
    
class SPError(Exception):
    pass
        
def start(idFileName, configFileName):
    """ Start the archiver, returning the resource termination interval if creation was successful. """
    config = parseConfigFile(configFileName)
    interval = rgma.TimeInterval(20, rgma.TimeUnit.MINUTES)
    storage = None
    if config.has_key(LOGICALNAME_TOKEN):
        storage = rgma.Storage.getDatabaseStorage(config[LOGICALNAME_TOKEN])
    else:
        raise SPError("config file has not specified a 'LogicalName' for the secondary producer storage")
    
    if config.has_key(TYPE_TOKEN):
        type = config[TYPE_TOKEN].upper()
        sq = list(set('C' + type))
        sq.sort()
        sq = "".join(sq)
        if not hasattr(rgma.SupportedQueries, sq):
            raise SPError("Type must contain only some of the letters C, L or H")
        sq = getattr(rgma.SupportedQueries, sq)
    else:
        raise SPError("config file did not specify a value for 'Type'")
    
    hrpMins = None
    if config.has_key(HIST_RETEN_TOKEN):
        try:
            hrpMins = int(config[HIST_RETEN_TOKEN])
        except ValueError, e:
            raise SPError("HRP should be expressed as an integer in minutes")
    if not config.has_key(TABLES_TOKEN):
        raise SPError("No Tables specified")
            
    try:
        sp = None
        sp = rgma.SecondaryProducer(storage, sq)
        ti = rgma.RGMAService.getTerminationInterval()
        
        tables = {}
        for name in config[TABLES_TOKEN].split():
            tables[name] = None
        for table in tables.keys():
            key = table.upper() + '_' + HIST_RETEN_TOKEN
            if config.has_key(key):
                hrp = rgma.TimeInterval(int(config[key]), rgma.TimeUnit.MINUTES)
            elif hrpMins:
                hrp = rgma.TimeInterval(hrpMins, rgma.TimeUnit.MINUTES)
            else:
                raise SPError("If HRP is not set globally it must be set on every table")
            key = table.upper() + '_' + PREDICATE_TOKEN
            if config.has_key(key):
                predicate = config[key]
            else:
                predicate = ''
            sp.declareTable(table, predicate, hrp)
        id = sp.getResourceId()
        f = open(idFileName, 'w')
        f.write('# Generated by rgma-sp\n')
        f.write(`id`)
        f.close()
    except ValueError, e:
        close(sp)
        raise SPError("HRP should be expressed as an integer in minutes")
    except rgma.RGMAException, e:
        close(sp)
        raise SPError("Could not create archiver, error message: " + e.args[0])
    except Exception, e:
        close(sp)
        raise SPError("Could not create archiver, error message: " + `e.args`)
    return int(float(ti.getValueAs()))

def close(sp):
    if (sp):
        try:
            sp.close()
        except rgma.RGMAException, e:
            pass
    
def stop(idFileName):
    """ Closes the resource identified by its endpoint. Deletes
        the archiver id file. """
    id = getId(idFileName)
    try:
         rgma.SecondaryProducer.staticClose(id)
    except rgma.RGMAException, e:
         raise SPError("Error occured: " + `e.args[0]`)
    os.remove(idFileName)

def ping(idFileName):    
    """ Sends a 'showSignOfLife' to the resource identified by its endpoint. 
        If the request fails, the script will exit with error code 1. """
    id = getId(idFileName)
    try:
        if not rgma.SecondaryProducer.staticShowSignOfLife(id):
            raise SPError("Secondary producer has vanished ...")
    except (rgma.RGMATemporaryException), e:
        pass
    except (rgma.RGMAPermanentException), e:
        raise SPError("Error occured: " + `e.args[0]`)
            
def getId(fname):
    """ Returns the id derived from the id file. It is taken from the 
    first line without a leading #. """
    if not os.path.isfile(fname):
         raise SPError("Error: id file " + fname + " does not exist")

    try:
        f = open(fname)
        lines = f.readlines()
        for line in lines:
            line = line.strip()
            if len(line) > 0 and not line.startswith('#'):
                return int(line)
    except (IOError), e:
        raise SPError("Error: reading " + fname)
    except (ValueError), e:
        raise SPError("Error: reading " + fname + ". It must contain just one integer.")
    
def parseConfigFile(fname):
    
    """ Parses the config file, returning any properties as a dictionary. """
    
    config = {}
    f = open(fname)
    lines = f.readlines()
    f.close()
    for line in lines:
        line = line.strip()
        if line and not line.startswith('#'):
            eqIndex = line.find("=")
            if eqIndex < 0:
                 raise SPError("Error: reading file " + fname)
            key = line[:eqIndex].strip().upper()
            value = line[eqIndex+1:].strip()
            config[key] = value
    return config
 
if __name__ == '__main__':
    main()
 
